Saltar al contenido principal

Guía de estilo para contratos inteligentes

Convenciones de nomenclatura

1. Visión arquitectónica

Nuestro protocolo utiliza una arquitectura modular de diamante/faceta, donde cada faceta encapsula un dominio específico (p. ej., intercambios Uniswap, creación de gardens, control de acceso). Las facetas exponen interfaces externas, delegan la lógica a contratos base internos y usan bibliotecas de almacenamiento para la gestión del estado. Este patrón permite la posibilidad de actualizaciones (upgradability), separación de responsabilidades y límites claros entre las funcionalidades del protocolo.
Ejemplo: Faceta Uniswap

  • Faceta externa: UniswapFacet expone funciones de swap y TWAP.
  • Interfaz: IUniswap define la API externa y los structs de parámetros.
  • Base interna: UniswapBase implementa la lógica central, sólo invocable por la faceta.
  • Biblioteca de almacenamiento: UniswapStorage gestiona el estado persistente.
  • Biblioteca de errores: IUniswapBase define errores y eventos.

2. Convenciones de nomenclatura

Contratos y Facetas

  • Contratos de faceta: Sufijo Facet (p. ej., UniswapFacet).
  • Contratos base: Sufijo Base (p. ej., UniswapBase).
  • Bibliotecas de almacenamiento: Sufijo Storage (p. ej., UniswapStorage).
  • Interfaces: Prefijo I (p. ej., IUniswap).
  • Interfaces base: Prefijo I y sufijo Base (p. ej., IUniswapBase).

Funciones

  • Funciones externas: Usar nombres descriptivos en camelCase (p. ej., swapExactInputSingleHop).
  • Funciones internas: Prefijar con _ (p. ej., _swapExactInputSingleHop).
  • Inicialización: Sufijo _init (p. ej., UniswapFacet_init).

Structs

  • Structs de parámetros: Sufijo Params (p. ej., GardenSwapParams).
  • Tipos compuestos: Usar nombres descriptivos (p. ej., TokenWithFee).

Errores

  • Prefijo de dominio: Prefijar con el nombre de la faceta (p. ej., UniswapFacet_InsufficientBalance).
  • Descriptivos: Usar PascalCase, describir el fallo (p. ej., UniswapFacet_SwapDeadlineHasPassed).

Eventos

  • Tiempo pasado: Usar pasado para cambios de estado (p. ej., UniswapFacetTokensSwapped).
  • Prefijo de dominio: Prefijar con el nombre de la faceta para mayor claridad.

Variables

  • Variables de estado: Privadas, con prefijo _ en contratos base, almacenadas en bibliotecas de almacenamiento.
  • Variables dentro de structs: Usar camelCase, descriptivas.

Carpetas

  • Facetas: Cada faceta debe tener una carpeta separada.
  • Descriptivas: Usar camelCase (p. ej., liquidityPoolRegistry).

/*###############################################################################

@title Diamond
@author BLOK Capital DAO

▗▄▄▖ ▗▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▄▖ ▗▖ ▗▄▄▄ ▗▄▖ ▗▄▖
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌▗▞▘ ▐▌ ▐▌ ▐▌▐▌ ▐▌ █ █ ▐▌ ▐▌▐▌ ▐▌ █▐▌ ▐▌▐▌ ▐▌
▐▛▀▚▖▐▌ ▐▌ ▐▌▐▛▚▖ ▐▌ ▐▛▀▜▌▐▛▀▘ █ █ ▐▛▀▜▌▐▌ ▐▌ █▐▛▀▜▌▐▌ ▐▌
▐▙▄▞▘▐▙▄▄▖▝▚▄▞▘▐▌ ▐▌ ▝▚▄▄▖▐▌ ▐▌▐▌ ▗▄█▄▖ █ ▐▌ ▐▌▐▙▄▄▖ ▐▙▄▄▀▐▌ ▐▌▝▚▄▞▘


################################################################################*/

Orden de disposición de los elementos del contrato inteligente

Los elementos del contrato deben disponerse en el siguiente orden:

  1. Sentencias pragma
  2. Sentencias import
  3. Eventos
  4. Errores
  5. Interfaces
  6. Bibliotecas (libraries)
  7. Contratos

Dentro de cada contrato, biblioteca o interfaz, use el siguiente orden:

  1. Declaraciones de tipos (type declarations)
  2. Variables de estado
  3. Eventos
  4. Errores
  5. Modificadores
  6. Funciones

Siga la guía oficial de Solidity para más detalles: link

Orden de las funciones

El orden ayuda a los lectores a identificar qué funciones pueden llamar y a encontrar el constructor y las definiciones receive / fallback más fácilmente.

Las funciones deben agruparse según su visibilidad y ordenarse:

  • constructor
  • función receive (si existe)
  • función fallback (si existe)
  • external
  • public
  • internal
  • private

Dentro de un mismo grupo, colocar las funciones view y pure al final.


// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract A {
constructor() {
// ...
}

receive() external payable {
// ...
}

fallback() external {
// ...
}

// External functions
// ...

// External functions that are view
// ...

// External functions that are pure
// ...

// Public functions
// ...

// Internal functions
// ...

// Private functions
// ...
}

Contrato de ejemplo

El siguiente bloque mantiene el código tal cual; los comentarios de documentación en inglés se pueden traducir si lo desea, pero se mantiene la sintaxis original para garantizar compatibilidad.

// SPDX-License-Identifier: GPL-3.0
/*###############################################################################

@title Diamond
@author BLOK Capital DAO

▗▄▄▖ ▗▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▄▖ ▗▖ ▗▄▄▄ ▗▄▖ ▗▄▖
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌▗▞▘ ▐▌ ▐▌ ▐▌▐▌ ▐▌ █ █ ▐▌ ▐▌▐▌ ▐▌ █▐▌ ▐▌▐▌ ▐▌
▐▛▀▚▖▐▌ ▐▌ ▐▌▐▛▚▖ ▐▌ ▐▛▀▜▌▐▛▀▘ █ █ ▐▛▀▜▌▐▌ ▐▌ █▐▛▀▜▌▐▌ ▐▌
▐▙▄▞▘▐▙▄▄▖▝▚▄▞▘▐▌ ▐▌ ▝▚▄▄▖▐▌ ▐▌▐▌ ▗▄█▄▖ █ ▐▌ ▐▌▐▙▄▄▖ ▐▙▄▄▀▐▌ ▐▌▝▚▄▞▘


################################################################################*/

pragma solidity ^0.8.24;

/*//////////////////////////////////////////////////////////////
IMPORTS
//////////////////////////////////////////////////////////////*/

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/*//////////////////////////////////////////////////////////////
CONTRACTS
//////////////////////////////////////////////////////////////*/

/// @title ProtocolTemplate
/// @notice A starting point for BLOK Capital protocol modules adhering to the style guide.
/// @dev Replace placeholders with domain-specific logic. Follow visibility and layout ordering strictly.
contract ProtocolTemplate {
/*//////////////////////////////////////////////
1) TYPE DECLARATIONS
//////////////////////////////////////////////*/

/// @notice Example struct for cohesive configuration/state.
struct Config {
// slot 0
address admin; // privileged controller (e.g., DAO multisig or Diamond facet admin)
// slot 1
uint96 feeBps; // example fee in basis points
bool paused; // simple circuit breaker
}

/*//////////////////////////////////////////////
2) STATE VARIABLES
//////////////////////////////////////////////*/

/// @notice Packed configuration/state.
Config private _cfg;

/// @notice Generic key-value parameters (optional pattern).
mapping(bytes32 => uint256) private _params;

/// @notice Example immutable version tag for off-chain tooling.
string public constant VERSION = "v0.1.0-template";

/*//////////////////////////////////////////////
3) EVENTS
//////////////////////////////////////////////*/
/// @notice Emitted when the admin address is updated.
/// @param oldAdmin The previous admin.
/// @param newAdmin The new admin.
event AdminUpdated(address indexed oldAdmin, address indexed newAdmin);

/*//////////////////////////////////////////////
4) ERRORS
//////////////////////////////////////////////*/
/// @notice Thrown when a caller that is not the admin invokes an admin-only function.
error NotAdmin();

/// @notice Thrown when an operation uses a zero address where it is not allowed.
error ZeroAddress();

/*//////////////////////////////////////////////
5) MODIFIERS
//////////////////////////////////////////////*/

/// @notice Restricts function to the current admin.
modifier onlyAdmin() {
if (msg.sender != _cfg.admin) revert NotAdmin();
_;
}

/// @notice Reverts when the contract is paused.
modifier whenNotPaused() {
if (_cfg.paused) revert InvalidParameter(LibKeys.K_PAUSED, 1);
_;
}

/*//////////////////////////////////////////////
6) FUNCTIONS
//////////////////////////////////////////////*/

//------------- Constructor -------------

/// @notice Initializes the template with an admin and optional initial params.
/// @param initialAdmin The admin address (DAO, multisig, or Diamond owner).
/// @param initialFeeBps Initial fee basis points (0–10_000).
/// @param initiallyPaused Whether the module starts paused.
constructor(address initialAdmin, uint96 initialFeeBps, bool initiallyPaused) {
if (initialAdmin == address(0)) revert ZeroAddress();
if (initialFeeBps > 10_000) revert InvalidParameter(LibKeys.K_FEE_BPS, initialFeeBps);

_cfg.admin = initialAdmin;
_cfg.feeBps = initialFeeBps;
_cfg.paused = initiallyPaused;

_params[LibKeys.K_FEE_BPS] = initialFeeBps;
_params[LibKeys.K_PAUSED] = initiallyPaused ? 1 : 0;
}

//------------- Receive (if exists) -------------

/// @notice Accepts ETH transfers.
receive() external payable {
// Intentionally empty. Consider emitting an event if tracking deposits is required.
}

//------------- Fallback (if exists) -------------

/// @notice Fallback for non-matching function selectors.
fallback() external payable {
// Intentionally empty. Consider delegating or reverting depending on module design.
}

//------------- External -------------

/// @notice Update the admin address.
/// @param newAdmin The new admin address.
/// @dev External to keep the interface surface clear; restricted via onlyAdmin.
function setAdmin(address newAdmin) external onlyAdmin {
if (newAdmin == address(0)) revert ZeroAddress();
address old = _cfg.admin;
_cfg.admin = newAdmin;
emit AdminUpdated(old, newAdmin);
}

/// @notice Update a numeric parameter by key.
/// @param key The parameter key (see LibKeys).
/// @param value The new value.
/// @dev Example: setting FEE_BPS or PAUSED.
function setParam(bytes32 key, uint256 value) external onlyAdmin {
uint256 old = _params[key];

// Example validation: enforce bounds for known keys.
if (key == LibKeys.K_FEE_BPS) {
if (value > 10_000) revert InvalidParameter(key, value);
_cfg.feeBps = uint96(value);
} else if (key == LibKeys.K_PAUSED) {
_cfg.paused = (value != 0);
}

_params[key] = value;
emit ParamUpdated(key, old, value);
}

//------------- External view -------------

/// @inheritdoc IProtocolTemplate
function admin() external view returns (address) {
return _cfg.admin;
}

/// @inheritdoc IProtocolTemplate
function getParam(bytes32 key) external view returns (uint256 value) {
return _params[key];
}

/// @notice Get the current fee in basis points.
function feeBps() external view returns (uint96) {
return _cfg.feeBps;
}

/// @notice Return whether the contract is paused.
function paused() external view returns (bool) {
return _cfg.paused;
}

//------------- Public -------------

/// @notice Example public function demonstrating the `whenNotPaused` guard.
/// @dev Replace with real business logic (e.g., deposit/withdraw/swap).
function exampleAction(uint256 amount) public whenNotPaused {
// Implement domain-specific logic. This is a stub.
// e.g., _takeFee(amount); _doSomething(amount);
amount; // silence compiler warning for template
}

//------------- Internal -------------

/// @notice Example internal helper to compute a fee.
/// @param amount The gross amount.
/// @return fee The computed fee based on current feeBps.
function _computeFee(uint256 amount) internal view returns (uint256 fee) {
unchecked {
fee = (amount * _cfg.feeBps) / 10_000;
}
}

//------------- Private -------------

// Add private helpers as needed, placed after internal functions.
}

Plantilla de contrato

La plantilla siguiente se mantiene en su forma original de código. Si desea, puedo traducir los comentarios /// @notice y otros comentarios en el código al español también.

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
/*###############################################################################

@title Enter contract title
@author BLOK Capital DAO
@notice Enter contract utility

▗▄▄▖ ▗▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▄▖ ▗▖ ▗▄▄▄ ▗▄▖ ▗▄▖
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌▗▞▘ ▐▌ ▐▌ ▐▌▐▌ ▐▌ █ █ ▐▌ ▐▌▐▌ ▐▌ █▐▌ ▐▌▐▌ ▐▌
▐▛▀▚▖▐▌ ▐▌ ▐▌▐▛▚▖ ▐▌ ▐▛▀▜▌▐▛▀▘ █ █ ▐▛▀▜▌▐▌ ▐▌ █▐▛▀▜▌▐▌ ▐▌
▐▙▄▞▘▐▙▄▄▖▝▚▄▞▘▐▌ ▐▌ ▝▚▄▄▖▐▌ ▐▌▐▌ ▗▄█▄▖ █ ▐▌ ▐▌▐▙▄▄▖ ▐▙▄▄▀▐▌ ▐▌▝▚▄▞▘


################################################################################*/


/*//////////////////////////////////////////////////////////////
IMPORTS
//////////////////////////////////////////////////////////////*/

/*//////////////////////////////////////////////////////////////
CONTRACTS
//////////////////////////////////////////////////////////////*/

contract ProtocolTemplate {
/*//////////////////////////////////////////////
1) TYPE DECLARATIONS
//////////////////////////////////////////////*/


/*//////////////////////////////////////////////
2) STATE VARIABLES
//////////////////////////////////////////////*/

/// constant
/// public
/// private
/// mapping

/*//////////////////////////////////////////////
3) EVENTS
//////////////////////////////////////////////*/


/*//////////////////////////////////////////////
4) ERRORS
//////////////////////////////////////////////*/


/*//////////////////////////////////////////////
5) MODIFIERS
//////////////////////////////////////////////*/


/*//////////////////////////////////////////////
6) FUNCTIONS
//////////////////////////////////////////////*/

//------------- Constructor -------------

//------------- Receive (if exists) -------------

//------------- Fallback (if exists) -------------

//------------- External -------------

//------------- External view -------------

//------------- Public -------------

//------------- Internal -------------

//------------- Private -------------

// Add private helpers as needed, placed after internal functions.
}